﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ClassToClassMapping.cs" >
//   J. Prinz
// </copyright>
// <summary>
//   Supports ClassToClass Mapping with predefined Mapping Libraries, containing MappingRoles and MappingClasses
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace AutomationMLMapper.AutomationML.Extensions
{
    using System.IO;
    using System.Linq;

    using AMLEngineExtensions;

    using CAEX_ClassModel;

    /// <summary>
    ///   Supports ClassToClass Mapping with predefined Mapping Libraries, containing MappingRoles and MappingClasses
    /// </summary>
    public static class ClassToClassMapping
    {
        #region Static Fields

        /// <summary>
        ///   Reference for the SystemUnitClassMapping - RoleClass. The Reference is valid when the "MappingRoleClassLib" is loaded
        /// </summary>
        public static string SystemUnitClassMappingRoleClassPath = "MappingRoleClassLib/SystemUnitClassMapping";

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// Assign a Target Class to a mapping object of a mapping class if no assignment already exists
        /// </summary>
        /// <param name="mappingClass">
        /// </param>
        /// <param name="targetClass">
        /// </param>
        /// <param name="systemUnitClassMappingObject">
        /// </param>
        /// <param name="systemUnitClassMappingRole">
        /// </param>
        /// <returns>
        /// The CAEX_ClassModel.InternalElementType. 
        /// </returns>
        public static InternalElementType AssignTargetClassToMappingObject(
            SystemUnitClassType mappingClass, 
            SystemUnitClassType targetClass, 
            SystemUnitFamilyType systemUnitClassMappingObject, 
            RoleFamilyType systemUnitClassMappingRole)
        {
            if (mappingClass != null && targetClass != null && targetClass != mappingClass
                && systemUnitClassMappingObject != null && systemUnitClassMappingRole != null)
            {
                var mappingObject = GetClassToTargetMappingObject(mappingClass, targetClass);
                if (mappingObject == null)
                {
                    mappingObject =
                        systemUnitClassMappingObject.CreateClassInstance().Node.CreateCAEXWrapper<InternalElementType>();
                    mappingClass.Insert_InternalElement(mappingObject);
                    mappingObject.Name.Value = targetClass.Name.Value + "Mapping";
                    mappingObject.New_RoleRequirements().RefBaseRoleClassPath.Value =
                        systemUnitClassMappingRole.CAEXPath();
                    mappingObject.SetAttributeValue("MappedSystemUnitClassPathReference", targetClass.CAEXPath());
                }

                return mappingObject;
            }

            return null;
        }

        /// <summary>
        /// Create a MappingClass for the provided SourceSystemUnitClass, the new MappingClass
        ///   will be inserted into the MappingClassLibrary
        /// </summary>
        /// <param name="sourceSystemUnitClass">
        /// </param>
        /// <param name="mappingClassLibrary">
        /// </param>
        /// <returns>
        /// The CAEX_ClassModel.SystemUnitFamilyType. 
        /// </returns>
        public static SystemUnitFamilyType CreateMappingClass(
            SystemUnitClassType sourceSystemUnitClass, SystemUnitClassLibType mappingClassLibrary)
        {
            if (sourceSystemUnitClass != null)
            {
                return CreateMappingClass(sourceSystemUnitClass.CAEXPath(), mappingClassLibrary);
            }

            return null;
        }

        /// <summary>
        /// Create a MappingClass for the a SystemUnitClass, referenced by a ClassPath, the new MappingClass
        ///   will be inserted into the MappingClassLibrary
        /// </summary>
        /// <param name="sourceSystemUnitClassPathReference">
        /// </param>
        /// <param name="mappingClassLibrary">
        /// </param>
        /// <returns>
        /// The CAEX_ClassModel.SystemUnitFamilyType. 
        /// </returns>
        public static SystemUnitFamilyType CreateMappingClass(
            string sourceSystemUnitClassPathReference, SystemUnitClassLibType mappingClassLibrary)
        {
            if (!string.IsNullOrEmpty(sourceSystemUnitClassPathReference) && mappingClassLibrary != null)
            {
                var mappingClassName = Path.GetFileName(sourceSystemUnitClassPathReference) + "Mapping";
                var mappingClass = GetMappingClass(sourceSystemUnitClassPathReference, mappingClassLibrary);
                if (mappingClass == null)
                {
                    mappingClass = mappingClassLibrary.New_SystemUnitClass(mappingClassName);
                    if (mappingClass != null)
                    {
                        mappingClass.RefBaseClassPath.Value = sourceSystemUnitClassPathReference;
                    }
                }

                return mappingClass;
            }

            return null;
        }

        /// <summary>
        /// Get the MappingObject from the Target Class, which is assigned to the target via its "MappedSystemUnitClassPathReference"-Attribute
        /// </summary>
        /// <param name="mappingClass">
        /// </param>
        /// <param name="targetClass">
        /// </param>
        /// <returns>
        /// The CAEX_ClassModel.InternalElementType. 
        /// </returns>
        public static InternalElementType GetClassToTargetMappingObject(
            SystemUnitClassType mappingClass, SystemUnitClassType targetClass)
        {
            return
                mappingClass.InternalElement.Cast<InternalElementType>().Where(IsClassToClassMappingObject).
                    FirstOrDefault(
                        ie => ie.GetAttributeValue("MappedSystemUnitClassPathReference") == targetClass.CAEXPath());
        }

        /// <summary>
        /// Get a MappingClass for the a SystemUnitClass, referenced by a ClassPath
        /// </summary>
        /// <param name="sourceSystemUnitClassPathReference">
        /// </param>
        /// <param name="mappingClassLibrary">
        /// </param>
        /// <returns>
        /// The CAEX_ClassModel.SystemUnitFamilyType. 
        /// </returns>
        public static SystemUnitFamilyType GetMappingClass(
            string sourceSystemUnitClassPathReference, SystemUnitClassLibType mappingClassLibrary)
        {
            if (!string.IsNullOrEmpty(sourceSystemUnitClassPathReference) && mappingClassLibrary != null)
            {
                var mappingClassName = Path.GetFileName(sourceSystemUnitClassPathReference) + "Mapping";
                return mappingClassLibrary.SystemUnitClass.GetCaexObjectByName(mappingClassName) as SystemUnitFamilyType;
            }

            return null;
        }

        /// <summary>
        /// Identify an Internal Element as a ClassMapping Object via its RoleRequirement
        /// </summary>
        /// <param name="internalElement">
        /// </param>
        /// <returns>
        /// The System.Boolean. 
        /// </returns>
        public static bool IsClassToClassMappingObject(InternalElementType internalElement)
        {
            return internalElement.RoleRequirements.Exists
                   && internalElement.RoleRequirements[0].RefBaseRoleClassPath.Exists()
                   &&
                   internalElement.RoleRequirements[0].RefBaseRoleClassPath.Value == SystemUnitClassMappingRoleClassPath;
        }

        #endregion
    }
}